package com.yami.shop.seckill.api.service.impl;

import cn.hutool.core.lang.Snowflake;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.IdUtil;
import com.yami.shop.common.util.RedisUtil;
import com.yami.shop.seckill.api.service.SeckillCacheManager;
import com.yami.shop.seckill.common.service.SeckillService;
import com.yami.shop.seckill.common.service.SeckillSkuService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * @author LGH
 */
@Slf4j
@Service
@AllArgsConstructor
public class SeckillCacheManagerImpl implements SeckillCacheManager {

    private final RedissonClient redissonClient;

    private static final String REDISSON_LOCK_PREFIX = "redisson_lock:";
    /**
     * 秒杀限流前缀
     */
    private final static String SECKILL_LIMIT_PREFIX = "SECKILL_LIMIT_";

    /**
     * 秒杀路径前缀
     */
    private final static String SECKILL_PATH_PREFIX = "SECKILL_PATH_";

    private final static String SECKILL_SKU_STOCKS_PREFIX = "SECKILL_SKU_STOCKS_";

    private final Snowflake snowflake;

    private final SeckillSkuService seckillSkuService;
    private final SeckillService seckillService;


    @Override
    public String createOrderPath(String userId) {

        String limitKey = SECKILL_LIMIT_PREFIX + userId;
        int maxCount = 5;

        // 秒杀次数+1
        long seckillNum = RedisUtil.incr(limitKey, 1);

        // 5秒只能提交5次请求
        if (seckillNum == 1) {
            // 5秒后失效
            RedisUtil.expire(limitKey, 5);
        }
        if (seckillNum >= maxCount) {
            // 请求过频繁，请稍后再试
            throw new YamiShopBindException("yami.request.too.much");
        }
        String orderPath = IdUtil.encode(snowflake.nextId());

        // 保存秒杀路径，5分钟这个路径就失效
        RedisUtil.set(getPathKey(userId), orderPath, 300);

        return orderPath;
    }

    @Override
    public void checkOrderPath(String userId, String orderPath) {
        String cacheOrderPath = RedisUtil.get(getPathKey(userId));

        if (!Objects.equals(cacheOrderPath, orderPath)) {
            // 订单已过期，请重新选择商品进行秒杀
            throw new YamiShopBindException("yami.order.expired");
        }
    }

    private String getPathKey(String userId) {
        return SECKILL_PATH_PREFIX + userId;
    }


    @Override
    public void decrSeckillSkuStocks(Long seckillSkuId, Long seckillId, Integer prodCount, Long prodId) {
        String key = SECKILL_SKU_STOCKS_PREFIX + seckillSkuId;

        Long cacheStocks = RedisUtil.getLongValue(key);

        if (cacheStocks != null && cacheStocks <= 0) {
            // 库存不足
            throw new YamiShopBindException("yami.insufficient.inventory");
        }

        // 如果没有库存就缓存一个库存
        if (cacheStocks == null || RedisUtil.getExpire(key) < 1) {

            // 加锁，防止缓存击穿
            RLock rLock = redissonClient.getLock(REDISSON_LOCK_PREFIX + ":getSeckillSkuStocks");
            try {
                int lockWait = 10;
                if (rLock.tryLock(lockWait, lockWait, TimeUnit.SECONDS)) {
                    // 再获取一遍缓存
                    cacheStocks = RedisUtil.getLongValue(key);
                    if (cacheStocks == null) {
                        seckillSkuService.removeSeckillCacheSkuById(seckillSkuId, seckillId);
                        seckillService.removeSeckillCache(seckillId, prodId);
                        Integer seckillStocks = seckillSkuService.getSeckillSkuById(seckillSkuId).getSeckillStocks();
                        if (seckillStocks > 0) {
                            RedisUtil.setLongValue(key, Long.valueOf(seckillStocks), -1);
                        } else {
                            RedisUtil.setLongValue(key, 0L, 30);
                        }
                        cacheStocks = Long.valueOf(seckillStocks);
                    }
                } else {
                    // 网络异常
                    throw new YamiShopBindException("yami.network.exception");
                }
            } catch (InterruptedException e) {
                log.error("InterruptedException:", e);
            } finally {
                try {
                    if (rLock.isLocked()) {
                        rLock.unlock();
                    }
                } catch (Exception e) {
                    log.error("Exception:", e);
                }
            }
        }

        if (cacheStocks == null || cacheStocks < prodCount || RedisUtil.decr(key, prodCount) < 0) {
            RedisUtil.expire(key, 30);
            // 本轮商品已被秒杀完毕，还有用户未支付，还有机会哟
            throw new YamiShopBindException("yami.seckill.complete");
        }
    }
}
